//###############
//FILE 			: FamilyMember.java
//WRITER	    : Lior Zimmerman, liorz06, 066462300
//WRITER 		: Ohad Dan, ohad_dan, 036767960
//EXERCISE 		: oop ex1 2008
//DESCRIPTION	: Holds family member data and handles all family actions.
//###############
package oop.ex1.family;
import java.util.List;

import oop.ex1.util.MyDate;

import java.util.ArrayList;
/**
 * Holds family member data and handles all family actions.
 */
public class FamilyMember implements 
oop.ex1.sortedStructure.Keyed<String>{
	private String _name;
	private Sex _sex;
	private MyDate _birthdate;
	private FamilyMember _father = null;
	private FamilyMember _mother = null;
	private List<FamilyMember> _children;
	private MartialStatus _martialStatus;
	private FamilyMember _partner;
	/**
	 * Constructs a new family member.
	 * @param name Family memeber's name.
	 * @param sex Family memeber's sex.
	 * @param birthdate Family memeber's birthdate.
	 * @throws FamilyException In case of null insertion.
	 */
	public FamilyMember(String name, Sex sex, MyDate birthdate)
	throws FamilyException{ 
		if ( name == null || name == "" ) {
			throw new FamilyNullException("Name of family member " +
					"is null or empty");
		}else if ( sex == null ){
			throw new FamilyNullException("The sex property is null");
		}else if( birthdate == null ){
			throw new FamilyNullException("Birthdate is null");
		}
		_name = name;
		_sex = sex;
		_birthdate = birthdate;
		_martialStatus = MartialStatus.SINGLE;
		_children = new ArrayList<FamilyMember>();
	} 
	/**
	 * Generates a string representing the family member.
	 * @return A string representation of the family member.
	 */
   public String toString(){
	   String info = _name + ": " + ( (_sex==Sex.MALE)?"male":"female" ) +
	   ", born on " + _birthdate.toString()+ ";";
	   if ( _father != null ) {
		   info += " father is " + _father.getName() +", ";
	   }
	   if ( _mother != null ) {
		   info += " mother is " + _mother.getName() + ", ";
	   }
	   String martial="";
	   switch( _martialStatus ) {
	   	case MARRIED:
		   martial = " married to " + _partner.getName()+ ", ";
		   break;
	   	case SINGLE:
	   		martial = " single, ";
	   		break;
	   	case DIVORCED:
	   		martial = " divorced, ";
	   		break;
	   	default:
	   		break;
	   }
	   String childStr = "no children";
	   if ( _children.size() == 1 ) {
	       childStr = "1 child";
	   }else if ( _children.size() > 1) {
	       childStr = _children.size() + " children ";
	   }
	   info += martial + childStr +".";
	   return info;
   }
   /**
    * Returns family member's key.
    * @return returns family member's key.
    */
   public String getKey() { 
	   return _name;
   }
   /**
    * Marries the family member.
    * @param other The about-to-be spouse.
    * @throws FamilyException In case of null insertion.
    */
   public void getMarried(FamilyMember other) throws FamilyException  {
       if ( other == null ) {
           throw new FamilyNullException("Spouse is null");
       }
	   if ( other.getMartialStatus() == MartialStatus.MARRIED || 
			   _martialStatus == MartialStatus.MARRIED ) {
		   throw new FamilyRelationException("Can't marry a married partner.");
	   }
	   setPartner( other );
	   other.setPartner( this);
	   setMartialStatus( MartialStatus.MARRIED );
	   other.setMartialStatus( MartialStatus.MARRIED );
	   
   }
   /**
    * Divorces the family member.
    * @throws FamilyException In case family member is not married.
    */
   public void getDivorced() throws FamilyException {
	   if ( _martialStatus != MartialStatus.MARRIED ) {
		   throw new FamilyRelationException("Can't Divorce if not married");
	   }
	   _partner.setPartner( null );
	   _partner.setMartialStatus( MartialStatus.DIVORCED );
	   setPartner( null );
	   setMartialStatus(MartialStatus.DIVORCED);
   }
   /**
    * Sets the martial status.
    * @param status Desirable martial status. 
    */
   private void setMartialStatus( MartialStatus status ) {
	   _martialStatus = status;
   }
   /**
    * Sets family member's partner.
    * @param partner Family member's.
    */
   private void setPartner( FamilyMember partner ) {
	   _partner = partner;
   }
   /**
    * Sets family member's father.
    * @param person Family member's father.
    * @throws FamilyException In case of null insertion.
    */
   public void  setFather(FamilyMember person) throws FamilyException {
	   if ( person == null ) {
		   throw new FamilyNullException("A father can't be a null value.");
	   }
       else if ( person == this ) {
       throw new FamilyRelationException("Family member can't be it's own" +
            "father.");
       }
	   _father = person;
	   person.addChild(this);
   }
   /**
    * Sets family member's mother.
    * @param person Family member's mother.
    * @throws FamilyException In case of null insertion.
    */
   public void  setMother(FamilyMember person) throws FamilyException{
	   if ( person == null ) {
		   throw new FamilyNullException("A mother can't be a null value.");
	   }else if ( person == this ) {
	       throw new FamilyRelationException("Family member can't be it's own" +
	       		"mother!");
	   }
	   _mother = person;
	   person.addChild(this);
   }
   /**
    * Retrieves family members sex.
    * @return family members sex.
    */
   public Sex getSex() {
	   return _sex;
   }
   /**
    * Retrieves family members name.
    * @return Family members sex.
    */
   public String getName(){
	   return _name;
   }
   /**
    * Ads a child.
    * @param child The family member to be added as a child.
    */
   private void addChild( FamilyMember child ){
	   if ( _children.isEmpty() ) {
	       _children.add(child);
	       return;
	   }
	   int i=1;
	   for( FamilyMember kid: _children ) {
	       if ( kid.getKey().compareTo( child.getKey() ) > 0 ) {
	           _children.add(i-1, child );
	           return;
	       }
	       i++;
	   }
	   _children.add(_children.size(), child);
   }
   /**
    * Retrieves a list with family member's children.
    * @return A list with family member's children.
    */
   public List<FamilyMember> getChildren(){
	   return _children;
   }
   /**
    * Retrives family member's father.
    * @return Family member's father.
    */
   public FamilyMember getFather() {
	   return _father;
   }
   /**
    * Retrives family member's mother.
    * @return Family member's mother.
    */
   public FamilyMember getMother() {
	   return _mother;
   }
   /**
    * Retrives family member's birth date.
    * @return Family member's birth date.
    */
   public MyDate getBirthdate() {
       return _birthdate;
   }
   /**
    * Retrives family member's spouse.
    * @return Family member's spouse.
    */
   public FamilyMember getSpouse() {
       return _partner;
   }
   /**
    * Retrives family member's martial status.
    * @return Family member's martial status.
    */
   public MartialStatus getMartialStatus() {
	   return _martialStatus;
   }
   /**
    * All possible sex for family member.
    */
   public enum Sex {
       MALE, FEMALE
   }
   /**
    * All possible marital status for family member.
    */
   public enum MartialStatus {
	   SINGLE, MARRIED, DIVORCED
   }
}